
package com.zcp.pcsuitall.business;

import com.zcp.pcsuitall.Action;
import com.zcp.pcsuitall.BusinessControl;
import com.zcp.pcsuitall.IDealWithMessage;
import com.zcp.pcsuitall.PictureDealWith;
import com.zcp.pcsuitall.R;
import com.zcp.pcsuitall.SuitUtil;
import com.zcp.pcsuitall.XmlDataDealWith;

import org.xmlpull.v1.XmlSerializer;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageStats;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.text.format.Formatter;
import android.util.Log;
import android.util.Xml;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.channels.SocketChannel;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class AppManager implements IDealWithMessage {

    public AppManager() {
        // nf.setMaximumFractionDigits(0);// 设置显示小数一位
    }

    @Override
    public boolean dealWith(SocketChannel sc, Action action, Context context) {
        boolean success = false;
        if (action != null) {
            if (LOAD.equals(action.getName())) {
                success = getAppInfo(sc, action, context);
            } else if (READ.equals(action.getName())) {
                success = getAppInfoByPackageName(sc, action, context);
            } else if (INSTALLAPK.equals(action.getName())) {
                InstallApk(sc, context, action);
            } else if("UnInstallApp".equals(action.getName()))
            {
                UnInstallApk(sc, context, action);
            }

        }
        return success;
    }

    private void InstallApk(SocketChannel sc, Context context, Action action) {
        String result = "fail";

        String apkPath = XmlDataDealWith.getRegularSingle("apkPath", action.getSource());
        File file = new File(apkPath);
        if (file.exists()) {
            result = "ok";
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setDataAndType(Uri.fromFile(new File(apkPath)),
                    "application/vnd.android.package-archive");
            context.startActivity(intent);
        }
        StringBuilder returnMessage = new StringBuilder();
        returnMessage.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        returnMessage.append("<root><action>InstallApp</action><data>");
        returnMessage.append("<result>" + result + "</result>");
        returnMessage.append("</data></root>");
        BusinessControl.returnToClient(sc, returnMessage.toString(), context);
    }

    private void UnInstallApk(SocketChannel sc, Context context, Action action) {
        String result = "fail";

        String packageName = XmlDataDealWith.getRegularSingle("package", action.getSource());
        if (packageName!=null&&(!packageName.trim().equals(""))) {
            result = "ok";
            Uri packageURI=Uri.parse("package:"+packageName);    
            Intent intent = new Intent(Intent.ACTION_DELETE,packageURI);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                   

            context.startActivity(intent);
        }
        StringBuilder returnMessage = new StringBuilder();
        returnMessage.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        returnMessage.append("<root><action>UnInstallApp</action><data>");
        returnMessage.append("<result>" + result + "</result>");
        returnMessage.append("</data></root>");
        BusinessControl.returnToClient(sc, returnMessage.toString(), context);
    }
    /**
     * 生成xml格式的string，这个用来包装应用程序信息。<br>
     * 用来生成类似下面xml对象，其中action 名字是固定的,value 可以变化，item文件信息。<br>
     * <?xml version="1.0" encoding="utf-8"?> <br>
     * <root><br>
     * <action>getExplorer</action><br>
     * <data><br>
     * <item><br>
     * <name>name</name><br>
     * <size>size</size><br>
     * <version>version</version><br>
     * <ico>icoStr</ico><br>
     * <package>package</package><br>
     * </item><br>
     * </data><br>
     * <from></from><br>
     * <to></to><br>
     * <total></total><br>
     * </root><br>
     * 
     * @param action 当其中action.getTotal().equals("notSetting")时候就是不用设置from to
     *            total 这三个参数
     * @param AppInfoObjectList 获取信息的数据。<br>
     * @return
     */

    protected String packageData(Action action, LinkedList<AppInfoObject> AppInfoObjectList) {
        StringWriter writer = new StringWriter();
        XmlSerializer serializer = Xml.newSerializer();
        try {
            serializer.setOutput(writer);
            XmlDataDealWith.createPre(action, serializer);
            if (AppInfoObjectList != null) {
                String ico = null;

                for (AppInfoObject element : AppInfoObjectList) {
                    serializer.startTag(SPACE, ITEM);

                    serializer.startTag(SPACE, NAME);
                    serializer.text(element.getName() != null ? element.getName() : SPACE);
                    serializer.endTag(SPACE, NAME);

                    serializer.startTag(SPACE, SIZE);
                    serializer.text(element.getSizeStr() != null ? element.getSizeStr() : SPACE);
                    serializer.endTag(SPACE, SIZE);

                    serializer.startTag(SPACE, VERSION);
                    serializer.text(element.getVersion() != null ? element.getVersion() : SPACE);
                    serializer.endTag(SPACE, VERSION);

                    serializer.startTag(SPACE, ICO);
                    ico = PictureDealWith.drawableToString(element.getIcon());
                    serializer.text(ico != null ? ico : NULL);
                    ico = null;
                    serializer.endTag(SPACE, ICO);

                    serializer.startTag(SPACE, PACKAGE);
                    serializer.text(element.getPackageStr() != null ? element.getPackageStr()
                            : SPACE);
                    serializer.endTag(SPACE, PACKAGE);

                    serializer.startTag(SPACE, "apkPath");
                    serializer.text(element.getSourcePath());
                    serializer.endTag(SPACE, "apkPath");

                    serializer.startTag(SPACE, "isSysApk");
                    serializer.text(SuitUtil.isSystemApp(element.getFlag()) + "");
                    serializer.endTag(SPACE, "isSysApk");

                    serializer.startTag(SPACE, isInstallSdcard);
                    serializer.text(SuitUtil.isInstallSdcard(element)+ "");
                    serializer.endTag(SPACE, isInstallSdcard);
                    
                    serializer.endTag(SPACE, ITEM);
                }
            }
            serializer.endTag(SPACE, DATA);
            // 如果total数据是notSetting，那么就是表示不用返回下面三个参数
            if (!action.getTotal().equals(NOSETTING)) {
                serializer.startTag(SPACE, FROM);
                serializer.text(action.getFrom());
                serializer.endTag(SPACE, FROM);

                serializer.startTag(SPACE, TO);
                serializer.text(action.getTo());
                serializer.endTag(SPACE, TO);

                serializer.startTag(SPACE, TOTAL);
                serializer.text(action.getTotal());
                serializer.endTag(SPACE, TOTAL);
            }

            serializer.endTag(SPACE, ROOT);
            serializer.endDocument();
            return writer.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return XmlDataDealWith.getSystemErrorString(action.getName(), e.toString());
        } finally {
            serializer = null;
            try {
                writer.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            writer = null;
        }
    }

    /**
     * 根据包名获取已经安装好应用程序信息。
     * 
     * @throws NameNotFoundException
     */
    private boolean getAppInfoByPackageName(SocketChannel sc, Action action, Context context) {
        boolean success = false;
        try {
            String inputPackageName = XmlDataDealWith.getRegularSingle("packname",
                    action.getSource());
            String readIco = XmlDataDealWith.getRegularSingle("readIco", action.getSource());
            String readSize = XmlDataDealWith.getRegularSingle("readSize", action.getSource());

            if (inputPackageName == null || inputPackageName.trim().equals(SPACE)) {
                BusinessControl.returnToClient(
                        sc,
                        XmlDataDealWith.getErrorString(action.getName(), "4",
                                context.getString(R.string.paramIsNull_4)), context);
                return false;
            }
            initializeAppinfo(context);

            LinkedList<AppInfoObject> AppInfoObjectList = new LinkedList<AppInfoObject>();
            String name = null;
            Drawable icon = null;
            String packageName = null;
            String size = "";
            for (int i = 0; i < appList.size(); i++) {
                if ("true".equals(readIco)) {
                    icon = appList.get(i).loadIcon(mPm);
                }
                name = appList.get(i).loadLabel(mPm).toString();
                packageName = appList.get(i).packageName;
                if (inputPackageName.equals(packageName)) {

                    size = "";
                    if ("true".equals(readSize)) {
                        size = getAppContainSize(appList.get(i), context);
                    }

                    AppInfoObjectList.add(new AppInfoObject(name, size, icon,
                            getVersion(packageName), packageName, appList.get(i).sourceDir, appList
                                    .get(i).flags));
                    break;
                }

            }

            if (AppInfoObjectList.size() <= 0) {
                BusinessControl.returnToClient(
                        sc,
                        XmlDataDealWith.getErrorString(action.getName(), "6",
                                context.getString(R.string.valuesNull_6)), context);
                return false;
            }

            action.setTotal(NOSETTING);
            String result = packageData(action, AppInfoObjectList);
            BusinessControl.returnToClient(sc, result, context);
        } catch (Exception e) {
            e.printStackTrace();
            BusinessControl.returnToClient(sc,
                    XmlDataDealWith.getSystemErrorString(action.getName(), e.toString()), context);
        }
        return success;

    }

    private static final String ZERO = "0";

    /**
     * 获取已经安装好应用程序信息。
     * 
     * @throws NameNotFoundException
     */
    private boolean getAppInfo(SocketChannel sc, Action action, Context context) {
        boolean success = false;
        LinkedList<AppInfoObject> AppInfoObjectList = new LinkedList<AppInfoObject>();
        try {
            String readIco = XmlDataDealWith.getRegularSingle("readIco", action.getSource());
            String readSize = XmlDataDealWith.getRegularSingle("readSize", action.getSource());
            initializeAppinfo(context);
            String name = null;
            Drawable icon = null;
            String packageName = null;
            int total = appList.size();
            int fromInt = Integer.valueOf(action.getFrom()).intValue();
            int toInt = Integer.valueOf(action.getTo()).intValue();
            action.setTotal(String.valueOf(total));
            // 取小的
            int count = toInt > total ? total : toInt;
            String size = "";
            if ((fromInt <= count) && (fromInt > 0)) {
                for (int i = fromInt - 1; i < count; i++) {
                    if ("true".equals(readIco)) {
                        icon = appList.get(i).loadIcon(mPm);
                    }
                    name = appList.get(i).loadLabel(mPm).toString();
                    packageName = appList.get(i).packageName;

                    if ("true".equals(readSize)) {
                        size = getAppContainSize(appList.get(i), context);
                    }

                    AppInfoObjectList.add(new AppInfoObject(name, size, icon,
                            getVersion(packageName), packageName, appList.get(i).sourceDir, appList
                                    .get(i).flags));              

                }
                action.setFrom(String.valueOf(fromInt));
                action.setTo(String.valueOf(count));
            } else {
                // 按接口定义，如果数据不符合要求那么都是返回0
                action.setFrom(ZERO);
                action.setTo(ZERO);
            }
            String result = packageData(action, AppInfoObjectList);
            BusinessControl.returnToClient(sc, result, context);

            name = null;
            icon = null;
            packageName = null;
        } catch (Exception e) {
            e.printStackTrace();
            BusinessControl.returnToClient(sc,
                    XmlDataDealWith.getSystemErrorString(action.getName(), e.toString()), context);
        } finally {
            AppInfoObjectList.clear();
            AppInfoObjectList = null;
        }
        return success;

    }

    /**
     * 获取系统版本号。
     * 
     * @param packageName
     * @return
     * @throws NameNotFoundException
     */
    private String getVersion(String packageName) throws NameNotFoundException {
        PackageInfo packInfo = mPm.getPackageInfo(packageName,
                PackageManager.GET_UNINSTALLED_PACKAGES);
        return packInfo.versionName;
    }

    /**
     * 读取应用程序大小(只是显示一位小数)。
     * 
     * @param appInfo
     * @return
     */
    private String getAppSize(ApplicationInfo appInfo, Context context) {
        String size = "0KB";
        if (appInfo.sourceDir != null) {
            size = Formatter.formatFileSize(context,
                    (new File((String) appInfo.sourceDir).length()));
            // Log.i(TAG, appInfo.sourceDir);
        }

        return size;
    }

    /**
     * 读取应用程序占用空间(只是显示一位小数)。
     * 这种通过反射调用系统隐藏方法(经过评审要求使用下面计算方法而不使用计算APK方法)，在升级的时候需要测试验证。
     * 
     * @param appInfo
     * @return
     */
    private String getAppContainSize(ApplicationInfo appInfo, Context context) {
        String size = "0KB";

        if (mySizeObserver != null) {
            CountDownLatch count = new CountDownLatch(1);
            String packageName = appInfo.packageName;
            mySizeObserver.invokeGetSize(packageName, count);
            try {
                count.await();
            } catch (InterruptedException e) {
                Log.i("AppManager", "Failed computing size for pkg : " + packageName);
            }
            // Process the package statistics
            PackageStats pStats = mySizeObserver.stats;
            boolean succeeded = mySizeObserver.succeeded;
            long total;
            if (succeeded && pStats != null) {
                total = getTotalSize(pStats);
            } else {
                total = SIZE_INVALID;
            }

            if (total != SIZE_INVALID) {
                size = Formatter.formatFileSize(context, total);
            }

        }

        return size;
    }

    /**
     * 用来动态计算应用程序大小的内部类。
     * 
     * @author Administrator
     */
    class MySizeObserver extends IPackageStatsObserver.Stub {
        private CountDownLatch mCount;

        private PackageManager myMPm;

        PackageStats stats;

        boolean succeeded;

        Method getPackageSizeInfo;

        public MySizeObserver(PackageManager currentmPm) {
            myMPm = currentmPm;
            try {
                getPackageSizeInfo = myMPm.getClass().getMethod("getPackageSizeInfo", String.class,
                        IPackageStatsObserver.class);
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        public void invokeGetSize(String packageName, CountDownLatch count) {
            mCount = count;
            try {
                getPackageSizeInfo.invoke(myMPm, packageName, this);
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        @Override
        public void onGetStatsCompleted(PackageStats pStats, boolean pSucceeded) {
            succeeded = pSucceeded;
            stats = pStats;
            mCount.countDown();
        }
    }

    private long getTotalSize(PackageStats ps) {
        if (ps != null) {
            return ps.cacheSize + ps.codeSize + ps.dataSize;
        }
        return SIZE_INVALID;
    }

    /**
     * 应用程序信息加载到appList变量中。
     * 
     * @param context
     */
    private void initializeAppinfo(Context context) {
        appList.clear();
        if (mPm == null) {
            mPm = context.getPackageManager();
        }
        if (mySizeObserver == null) {
            mySizeObserver = new MySizeObserver(mPm);
        }

        List<ApplicationInfo> installedAppList = mPm
                .getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);

        for (ApplicationInfo appInfo : installedAppList) {
            appList.add(appInfo);
        }
        // 默认按包名排序
        Collections.sort(appList, new MyComparator());
    }

    class MyComparator implements Comparator<ApplicationInfo> {

        @Override
        public int compare(ApplicationInfo object1, ApplicationInfo object2) {
            // TODO Auto-generated method stub
            return object1.packageName.compareTo(object2.packageName);
        }

    }

    private String LOAD = "LoadApplication";

    private String READ = "ReadApplicationInfo";

    private String INSTALLAPK = "InstallApp";

    private PackageManager mPm;

    private MySizeObserver mySizeObserver;

    // private NumberFormat nf = NumberFormat.getInstance();

    private LinkedList<ApplicationInfo> appList = new LinkedList<ApplicationInfo>();

    private String SPACE = "";

    private String ITEM = "item";

    private String NAME = "name";

    private String SIZE = "size";

    private String VERSION = "version";

    private String ICO = "ico";

    private String NULL = "null";

    private String PACKAGE = "package";

    private String DATA = "data";

    private String FROM = "from";

    private String TO = "to";

    private String TOTAL = "total";

    private String ROOT = "root";

    private String NOSETTING = "notSetting";

    private long SIZE_INVALID = -1;
    
    private String isInstallSdcard = "isInstallSdcard";
}
